General Remarks
- We suggest to read the following tutorials before you start implementing:
- Study the JacORB Programming Guide. Specifically chapters 4 and 5 are interesting for this lab. You can find it in the JacORB distribution in
doc/ProgrammingGuide.pdf.
- The JacORB bank demo shows how to work with implicit transactions and is a good reference for this lab. You can find it in the JacORB distribution in
demo/bank/transaction/implicit.
- IDL overview: Provides a good overview over the Interface Definition Language.
- Java IDL Mapping: Describes the mapping from IDL to Java.
- Java CORBA Introduction: This is a nice introduction for using CORBA with Java. Note that it's not JacORB specific!
- Group work is NOT allowed in the
lab. You have to work alone. Discussions with colleagues (e.g., in the
forum) are allowed but the code has to be written alone.
- Be sure to check the Tricky Parts section for questions!
Submission Guide (for all Labs)
Submission
- You must upload your solution using the Teaching Tool before the submission deadline: 15.01., 18:00 cet.
- the deadline is hard! You are responsible for submitting your
solution in time. If you do not submit, you won't get any points!
- Upload your solution as a ZIP file. Please
submit only the sources including your build and idl file (see below)
of your solution (not the compiled class files and no third-party
libraries).
- Your submission must compile and run in our lab environment. Use and complete the provided ant template.
- Before the submission deadline, you can upload your solution as often as you like. Note that any existing submission will be replaced by uploading a new one.
- Please make sure that your upload was successful (i.e., you
should be able to download your solution - as the tutors will do during
the interview).
Interviews
- After the submission deadline, there will be a mandatory
interview (Abgabegespräch). You must register for a time slot to the
interviews using the Teaching Tool.
- You can do the interview only if you submitted your solution before the deadline!
- The interview will take place in the DSLab. During the interview, you will be asked about the solution that you uploaded (i.e., changes after the deadline will not be taken into account!). In the interview you need to explain your code, design and architecture in detail.
- Remember that you can do the interview only once!
Description
In this assignment you will learn:
- the basics of CORBA (Common Object Request Broker Architecture)
- how to write a CORBA solution by starting with an IDL (Interface Description Language)
- how to generate Java artifacts from CORBA
- how to implement simple CORBA objects and clients
- how to use implicit transactions in CORBA
Overview
CORBA objects are hosted by server applications that provide one or
more object instances to clients. The communication of both the client
and the server is done by using so called ORBs
(Object Request Brokers) that deal with marshaling (encoding arguments
in a platform independent-format) and unmarshaling (decoding
platform-independent data type representations into platform specific
formats) and invocation of CORBA operations. One primary advantage of
CORBA compared to other technologies such as RMI, .NET Remoting, or Web
services is its coverage of services. That is, CORBA supports the full
set of additional services that are required in most distributed system
environments such as naming and trading, transaction and
synchronization, notification and security services.
In this assignment we will use CORBA to implement a
short messaging service (sms) infrastructure for telephone companies
(telcos). Since it must be possible for customers of one telco to send
messages to customers of other telcos, these telcos must be able to
communicate with each other. Each telco can have its own IT
infrastructure, possibly implemented in different languages on
different platforms. Therefore, a standard communication technology has
to be used for integrating these heterogeneous solutions. We choose
CORBA to realize such a telco system.
Domain assumptions:
- A telco is a telephone company which
provides its customers a service to send short messages. Each telco has
a name and its own unique prefix which consists of several digits (but
at least one) (e.g.
"0664").
- Messages contain text of arbitrary length
(quite contradictory to sms, but not relevant for our purpose) and get
a timestamp on sending. Messages may be sent to multiple recipients,
who may be customers of different telcos.
- A telco internal number is a telephone number that is only unique within a telco's scope and consists of several digits (but at least one) (e.g. "
123456"). A full number is a telephone number that is unique accross telcos, i.e., it consists of a prefix and a telco internal number (e.g. "0664/123456").
- Customer authentication is based on the telco internal number together with a four digit PIN code.
Architectural considerations:
In
this assignment we won't have such a simple client-server-architecture
as before.
Instead there might be several different telcos, that have to
communicate with other telcos over a well defined, public interface and
with their clients over a telco specific interface.
So each telco has to serve two purposes: on the one
side it has to deal with messages sent by its own customers and
potentially has to forward it to other telcos, on the other side it
must be able to handle messages coming from other telcos and forward
them to its own customers.
So customers may login, logout and send messages using the internal
interface of their telco. "Internal" in this context means "telco
internal", therefore this communication could be handled using any
possible protocol or implemented in any programming language (we will
also use CORBA and Java for this).
It first gets exciting when the
telco server has to communicate with other telcos (for example if a
recipient of a message is not a customer of the sender's telco) over a
well defined public interface, that each telco has to
support. To make it even more interesting, the delivery of the message
to all recipients must be handled within an (implicit) CORBA
transaction, i.e., either all recipients receive the message, or none.
To
sum it up, each telco has an internal interface for serving its own
customer requests, and an external interface for forwarding incoming
messages to its own customers.
So when a message is received on a telco, it has to
deliver the message to the recipients. If the concerned recipient is
currently online, the telco has to immediately forward it (therefore we
need client callbacks again), otherwise it has to
store it temporarily and transmit it the next time the recipient goes
online. However, it's not necessary to store the state persistently
when the telco is shut down.
As already said in the last assignment, most
distributed object frameworks provide a naming service, which allows
binding/looking up remote references to/by simple names. CORBA provides
one (org.omg.CosNaming.NamingContextExt),
and we will use it for binding telco references to their prefixes,
allowing customers to find their telco and telcos to find other telcos.
As you will see this happens quite similar as with the java.rmi.registry.Registry in RMI.
The following figure shows an example scenario:
 |
| Figure 1: Telco Architecture |
All operations displayed in red have to be executed within one single transaction (started in 7'),
the one shown using a dashed line is a notification (thus called on the
client callback object). The mailbox itself is not a stand-alone
communication partner (such as client or server) - it's meant as a
server-side data structure for storing missed messages of a single
customer (anyway, you may implement this requirement however you want).
Please note that the order of some operations (e.g. 9/10/11) is
interchangeable and solely depends on your implementation decisions.
Server
In this assignment the server is simply a specific implementation of
a telco. It allows customers to login, send sms and logout, as well as
other telcos to transmit messages to this telco's recipients.
Arguments
Your server program shall expect exactly one argument (if missing, print a usage message and exit):
fileName: specifies the name for a properties file that can be found on the classpath.
The properties file then should be read in from the classpath (see the hint section for details).
The format of properties file looks like the following:
#prefix for the telco, also used for binding it to name service
prefix:0664
#name of the telco
name:B2
#customer data follows (telco internal number:pin pairs):
123456:1234
234567:2345
345678:3456
...
An example file is provided and can be downloaded here. It contains the prefix (which shall be used for binding the telco object) and name
of the telco to be started, as well as all customer data in form of
telco internal number and pin code pairs. You can expect that there's
no other text in it, so simply filter out the prefix and name and then
read all accounts.
Interfaces
In CORBA you always have to define your interfaces in IDL files, thus, create the file telco.idl in your source folder (the ant template
provided expects this path and name) and specify your interfaces, data
structures etc. there. This specification is platform neutral and can
be translated by IDL compilers to platform specific code (in our case:
Java).
Public telco interface
This is
the interface every telco has to implement to assure compatibility with
other telcos. Normally, this interface would be given to you by some
standardization group, but for our lab you also have to define it.
Required functionality:
Get the name of the telco.
Get the prefix of the telco.
Deliver message within a transaction:
Allows
other telcos to submit messages (consisting of text, the sender's full
number and sending timestamp) to one or several recipients of this
telco (in form of telco internal numbers). The telco has to forward the
message to all online recipients and store it temporarily for offline
recipients. However, both ways must be implemented transaction aware
(see this for help)! In case a recipient is unknown or any internal error occurs the telco may raise an exception.
Internal telco interface
Extend
the public interface to implement your own telco service. For any
operation but login the client must be already logged in - you can
check this either by using a session identifier or by letting the
client submit its user name and password for each operation).
Required functionality:
Login of a customer:
The client/customer has to supply its telco internal number, pin code and a callback object (see client description). The server has to check whether the submitted values are valid and return all missed messages.
Logout of a customer:
From now on the server has to store any incoming message for this customer until the next time he/she goes online.
Send message as a single transaction:
Lets
the client send a message. The server has to create a transaction,
possibly deliver the message to other telcos, possibly store the
message for its own offline recipients and deliver it to its own online
recipients. In case of any error (such as unknown number, unknown telco
prefix or any failure, also of other telcos) the transaction must be
rolled back, otherwise committed. You shall use the implicit
transaction propagation model as described here. Note that also the sender may be a recipient.
Implementation details
On startup you first have to read in the properties file. Then
create your telco object, make it remotely available by exporting it
and bind it to the naming service using a NameComponent containing the prefix as id and "telco" as kind.
When a customer wants to send a message, your telco implementation has to create a transaction (see the JacORB
bank example). Then it has to map recipients to telcos (using the full
number's prefix) and look up the telcos in the name service (again by
supplying the prefix). You shall cache already looked up telco
references to avoid unnecessary lookups. If any problem occurs (such as
telco could not be found in name service, telco does not know recipient
number), you have to rollback the transaction, otherwise commit it.
Note that also already looked up telcos might have gone offline - if a org.omg.CORBA.TRANSIENT or org.omg.CORBA.COMM_FAILURE exception occurs you may assume the telco is not reachable and have to rollback the transaction.
When the telco itself is part of a transaction
(either when called by another telco or when a recipient of a message
sent by an own customer is also an own customer), you first have to
check whether such a number is known. If not, throw an appropriate
exception (which will cause the transaction to rollback). If the
recipient is currently online you have to transmit the message directly
over the client callback (see below), otherwise store the message.
Clients may also go offline without informing the server appropriately
(for example if the computer crashes or the connection is lost). You
don’t have to detect this at once, but the next time the server is
trying to contact the client to deliver a message: if a org.omg.CORBA.TRANSIENT or org.omg.CORBA.COMM_FAILURE
exception occurs you may assume the client is offline. Therefore update
the client's state and store the message to deliver it the next time
the client goes online again.
As in the last assignment the telco has to manage state across several threads (they are managed by JacORB transparently). This again makes synchronization necessary when accessing your shared data structures!
If your server is ready for handling requests print “Server <prefix> up. Hit enter to exit.” to the console and implement this behavior. On exit unbind the telco corba object from the naming service and also call ORB.shutdown(), otherwise your program might not shut down – again you must not use System.exit().
Client
In this part you have to build an interactive command line client application for your telco implementation.
Arguments
Again only one argument has to be specified (if missing, print a usage message and exit):
prefix: the prefix of the telco to contact. The supplied value will be used for resolving the telco object in the naming service.
Callback interface
As you've already noticed, a telco has to inform its online
recipients immediately on incoming messages. If you've solved the last
assignment, you are already familiar with the callback concept - in
CORBA it basically works exactly the same way as in RMI. To make it
short: you have to define an interface in your IDL file (telco.idl) which your client has to implement.
Required functionality:
Receive message within a transaction:
This
method can be called by the telco to inform the client about a message.
The client has to output the message to the console, containing the
sending time, the sender's full number and the text (e.g. "20.10.2008 @ 12:01 | 0664/123456: This is a test message."). Please note that the client notification must also be part of the CORBA transaction originally issued by the sender's telco.
Implementation details
On startup of your program you first have to look up your telco in the name service using the specified prefix. Note that you can only cast the looked up org.omg.CORBA.Object instance by using the *Helper.narrow() method. If the telco was looked up successfully print "Contacted telco "<telcoName>" (<telcoPrefix>) successfully."
to the console. Afterwards export your client callback object like you
exported your telco server object. The only difference is that you
should not bind it to the name service, but instead simply pass it as
parameter for the login command.
Interactive commands
The following commands must be supported by your client application.
Take care of handling invalid commands and arguments and provide usage
messages in these cases. Print meaningful error messages whenever the
server throws an “expected” exception (such as “Invalid login data.”
when trying to login with an invalid number/pin combination). Also
print success messages if an operation was executed successfully.
Note that you first have to login before sending sms (and, obviously, before logging out).
login <telcoInternalNumber> <pin>
Logs the user in. The telco server has to return all missed messages.
The output should include the same information as displayed below:
:> login 123456 1234
Logged in successfully.
Missed messages:
20.10.2008 @ 12:01 | 0699/123456: This is a test message.
20.10.2008 @ 12:05 | 0699/123456: This is also an extremely exciting test message.
logout
Logs out the currently logged in user.
sendSms "<text>" [<telPrefix>/<telNumber>]+
Sends an sms containing the specified text (all text between
(exclusive) the first two occuring quotes)) to a list (containing at
least one) of space separated recipients (in form of full numbers in
the specified format).
:> sendSms "This is a test message." 0664/123456 0664/234567
Message sent successfully.
stop
Stops the client application. If a user is currently logged in you have to log it out implicitly. Again you may not use System.exit(), but have to orderly close all acquired resources (shut down the ORB).
Ant template
As in the last assignment we provide a template build file (build.xml) in which you only have to adjust some class names. Put your source into the subdirectory "src", place the *.properties files and telco.idl into the "src" directory and move on the command line to the directory where the build file is located.
Simply type "ant idl-compile" for idl compilation (compiles the telco.idl file) and "ant" for java compilation (this task also copies your .properties files to the build directory).
Type "ant run-server -Dprops=b2" to start a server reading from b2.properties. In the provided build file an argument is expected for the server (<arg value="${props}.properties"/>). By specifying the -Dprops=b2 option, ant actually passes "b2.properties" as an argument to your server.
Type "ant run-client -Dprefix=0664" to start a client that connects to a telco bound to 0664 (by substituting <arg value="${prefix}"/> in the build file by "0664").
Put the src directory including telco.idl and build.xml into your submission.
Note that it's absolutely required that we are able to start your programs with these predefined commands!
Transactions help
How to provide transaction support in your telco/client
As the deliver message operation of the public telco interface and
the message received operation of the client callback interface have to
be executed within transactions, the following problem arises:
According to the OMG Transaction Service Specfication
a resource may only be involved in one transaction at the same time.
However, we want our client and especially our telco to be able to
receive messages from different transactions concurrently, otherwise
the performance would be quite bad.
So don't let your public telco interface and client callback directly extend CosTransaction::Resource and CosTransaction::TransactionalObject,
and instead create an additional interface that extends these both and
only provides a simple method that takes a message parameter
(containing sending time, sender's full number and text).
On the client's side it becomes now
simple. Provide an implementation for the specified resource interface,
which on commit simply outputs the received message. In your callback
you then create a new instance of this for each incoming request, hand
over the message, and register it with the coordinator.
On the telco's side it's a bit more
complicated, since there might be several recipients that might be
online or offline. If a recipient is online - there's no problem, just
forward it to the callback object (which handles the transaction as
described above). But if we have to store it for a recipient, we also
have to consider the transaction semantics. Therefore also provide an
implementation for the specified resource interface, which on commit
stores the message to the recipient's mailbox. In your telco do the
same as on the client's side (create resource, hand over message,
register it with coordinator).
For your information:
The CosTransaction::TransactionalObject
is just a markup interface and hence has no operations at all (primary
a historic relict from previous CORBA standards). However, you have to
implement the 5 operations of the Resource interface. You can skip implementing the forget() operation and implement the commit_one_phase() operation as in the JacORB sample. The prepare() method shall return one of the three values Vote.VoteCommit, Vote.VoteRollback, Vote.VoteReadOnly depending if the changes shall be committed, or rolled back. Vote.VoteReadOnly shall be used if there were no changes at all.
Implicit transactions
Using implicit transactions means that transaction contexts storing
the current state of a transaction are not directly visible in client
or server code but is automatically added by so-called interceptors -
in CORBA interceptors hook in requests and may modify the transmitted
data such as arguments or may add service contexts such as in our case.
To inform an ORB that it shall use the already
predefined interceptor for implicit transactions the following value
for the property org.omg.PortableInterceptor.ORBInitializerClass.TSClientInit must be set during the ORB initialization: org.jacorb.transaction.TransactionInitializer. This is a class that registers an object of type org.omg.CosTransactions.Current as an initial reference named TransactionCurrent. This reference can then be retrieved with orb.resolve_initial_references. The provided ant template already sets this property (see the run-server and run-client task).
The begin operation of the org.omg.CosTransactions.Current
interface may be used to initiate a transaction. The timeout shall be
set to a value of 40. This has to be set before you begin a
transaction. After a transaction has begun you can call whatever
transaction aware functionality of CORBA objects you want. The
transaction context is transmitted implicitly. For rolling back a
transaction you have to call the rollback function (surprisingly). Committing the transaction is done via the commit
operation (use true as argument). Never kill a server (regardless how)
that has started a transaction before the timeout has passed. When you
do manual tests you can set the timeout to a smaller value.
From transaction aware objects you have to use the org.omg.CosTransactions.Current interface, too. However, this time you shall receive the org.omg.CosTransactions.Control interface via the Current's get_control() operation. Control supports two different functionalities: returning a Coordinator (with get_coordinator()) and returning a Terminator (with get_terminator()). In transaction aware operations you have to register the transaction aware CORBA object with the coordinator's register_resource() operation. The Resource's
operations (prepare, rollback, commit) are then used automatically when
the transaction initiator calls commit or rollback. In case of any TransactionServer related exception (example: Inactive) throw the runtime exception org.omg.CORBA.TRANSACTION_ROLLEDBACK. To get the exact syntax of the transaction server related operations take a look at the CosTransactions.idl or study the OMG Transaction Service Specification.
IDL compilation
For compiling your IDL file, which uses transaction service interfaces, you have to
- write
#include <CosTransactions.idl> at the top of your IDL file to include the interface definitions
- and therefore tell the JacORB IDL compiler where to find the required IDL file
<CosTransactions.idl>: Use the -I option (e.g. -I/opt/jacorb/idl/omg/) to do this. If you use our ant template this option is already included (take a look at the idl-compile target).
Hints & Tricky Parts
Further Reading Suggestions
- APIs:
- Specifiations
- OMG CORBA Specification: This is the original OMG CORBA Specification. You can use it as a reference if you are specially interested in CORBA.
- OMG Transaction Service Specfication:
This is the original OMG Transaction Service Specification. In general,
it is not necessary to read all of this. However, in Section 2 all
involved CORBA interfaces are explained (as Resource, Control,
Coordinator, Terminator).
- OMG IDL to Java Mapping Specification: The official OMG document about the mapping from IDL to Java.
- Tutorials:
- CORBA Callbacks: Simple tutorial for callbacks (not JacORB specific).
- Java CORBA Guide: Here you can find several informations about CORBA with Java; again this is not related to using JacORB.
- Proposed Literature:
- Advanced CORBA(R) Programming with C++ by by Michi Henning and Steve Vinoski